# sht30.py - SHT30温湿度传感器插件（独立I2C总线版本）
import uasyncio as asyncio
import time
from machine import Pin, I2C

class SHT30:
    """SHT30温湿度传感器插件 - 使用独立I2C总线"""
    
    # SHT30 I2C地址
    DEFAULT_ADDRESS = 0x44  # 也可能是0x45，取决于ADDR引脚
    SECONDARY_ADDRESS = 0x45
    
    # SHT30命令
    CMD_SINGLE_HIGH = bytearray([0x2C, 0x06])  # 单次测量，高重复性，时钟拉伸
    CMD_SINGLE_MEDIUM = bytearray([0x2C, 0x0D])  # 单次测量，中重复性，时钟拉伸
    CMD_SINGLE_LOW = bytearray([0x2C, 0x10])  # 单次测量，低重复性，时钟拉伸
    CMD_SINGLE_HIGH_NOSTRETCH = bytearray([0x24, 0x00])  # 单次测量，高重复性，无时钟拉伸
    CMD_SINGLE_MEDIUM_NOSTRETCH = bytearray([0x24, 0x0B])  # 单次测量，中重复性，无时钟拉伸
    CMD_SINGLE_LOW_NOSTRETCH = bytearray([0x24, 0x16])  # 单次测量，低重复性，无时钟拉伸
    CMD_SOFT_RESET = bytearray([0x30, 0xA2])  # 软复位
    CMD_CLEAR_STATUS = bytearray([0x30, 0x41])  # 清除状态寄存器
    CMD_READ_STATUS = bytearray([0xF3, 0x2D])  # 读取状态寄存器
    
    def __init__(self, loader):
        """初始化插件"""
        self.loader = loader
        self.i2c = None  # 独立的I2C实例
        self.address = None
        self.running = True
        
        # GPIO配置
        self.sda_pin = 6  # GPIO6
        self.scl_pin = 7  # GPIO7
        
        # 配置参数
        self.read_interval = 5  # 读取间隔（秒）
        self.temp_threshold = 36.0  # 温度阈值（触发事件）
        self.humidity_threshold = 70.0  # 湿度阈值（触发事件）
        
        # 状态数据
        self.last_temp = None
        self.last_humidity = None
        self.last_read_time = 0
        self.error_count = 0
        self.max_errors = 5
        self.read_count = 0  # 读取计数
        
        # 插件能力声明
        self.capabilities = ["sht30", "temperature", "humidity", "environment"]
        
        print("[SHT30] 插件初始化")
    
    async def init(self):
        """异步初始化"""
        try:
            # 创建独立的I2C实例
            print("[SHT30] 创建独立I2C总线 (SDA:GPIO%d, SCL:GPIO%d)" % (self.sda_pin, self.scl_pin))
            
            # 使用软件I2C，明确指定引脚
            self.i2c = I2C(
                -1,  # 使用软件I2C
                scl=Pin(self.scl_pin),
                sda=Pin(self.sda_pin),
                freq=100000  # 100kHz标准速度
            )
            
            # 等待I2C总线稳定
            await asyncio.sleep_ms(100)
            
            # 扫描I2C设备
            devices = self.i2c.scan()
            print("[SHT30] I2C扫描结果: %s" % [hex(d) for d in devices])
            
            # 查找SHT30设备
            if self.DEFAULT_ADDRESS in devices:
                self.address = self.DEFAULT_ADDRESS
            elif self.SECONDARY_ADDRESS in devices:
                self.address = self.SECONDARY_ADDRESS
            else:
                print("[SHT30] 未找到SHT30设备 (0x44或0x45)")
                await self.loader.send_log('error', 'SHT30传感器未找到', {
                    'sda_pin': self.sda_pin,
                    'scl_pin': self.scl_pin,
                    'scanned_devices': [hex(d) for d in devices]
                })
                return False
            
            print("[SHT30] 找到设备，地址: 0x%02x" % self.address)
            
            # 软复位传感器
            await self.reset()
            await asyncio.sleep_ms(50)
            
            # 清除状态寄存器
            try:
                self.i2c.writeto(self.address, self.CMD_CLEAR_STATUS)
                await asyncio.sleep_ms(10)
            except:
                pass
            
            # 测试读取
            temp, humidity = await self.read_sensor()
            if temp is not None and humidity is not None:
                print("[SHT30] 初始读数 - 温度: %.2f°C, 湿度: %.2f%%" % (temp, humidity))
                await self.loader.send_log('info', 'SHT30初始化成功', {
                    'temperature': temp,
                    'humidity': humidity,
                    'address': hex(self.address),
                    'sda_pin': self.sda_pin,
                    'scl_pin': self.scl_pin
                })
                
                # 注册命令处理器
                self.loader.register_command('sht30_read', self.handle_read_command)
                self.loader.register_command('sht30_config', self.handle_config_command)
                self.loader.register_command('sht30_status', self.handle_status_command)
                
                return True
            else:
                print("[SHT30] 初始化读取失败")
                await self.loader.send_log('error', 'SHT30初始读取失败')
                return False
                
        except Exception as e:
            print("[SHT30] 初始化异常: %s" % e)
            await self.loader.send_log('error', 'SHT30初始化异常: %s' % str(e))
            return False
    
    async def reset(self):
        """软复位传感器"""
        try:
            self.i2c.writeto(self.address, self.CMD_SOFT_RESET)
            await asyncio.sleep_ms(20)
            print("[SHT30] 软复位完成")
        except Exception as e:
            print("[SHT30] 复位失败: %s" % e)
    
    def crc8(self, data):
        """计算CRC8校验值"""
        crc = 0xFF
        for byte in data:
            crc ^= byte
            for _ in range(8):
                if crc & 0x80:
                    crc = (crc << 1) ^ 0x31
                else:
                    crc = crc << 1
                crc &= 0xFF
        return crc
    
    async def read_sensor(self):
        """读取温湿度数据"""
        try:
            # 发送单次测量命令（高重复性，无时钟拉伸）
            self.i2c.writeto(self.address, self.CMD_SINGLE_HIGH_NOSTRETCH)
            
            # 等待测量完成（高重复性约15ms）
            await asyncio.sleep_ms(20)
            
            # 读取6字节数据（温度2字节+CRC1字节+湿度2字节+CRC1字节）
            data = self.i2c.readfrom(self.address, 6)
            
            # 验证CRC
            if self.crc8(data[0:2]) != data[2]:
                print("[SHT30] 温度CRC校验失败")
                return None, None
            
            if self.crc8(data[3:5]) != data[5]:
                print("[SHT30] 湿度CRC校验失败")
                return None, None
            
            # 转换温度数据
            temp_raw = (data[0] << 8) | data[1]
            temperature = -45 + (175 * temp_raw / 65535.0)
            
            # 转换湿度数据
            humidity_raw = (data[3] << 8) | data[4]
            humidity = 100 * humidity_raw / 65535.0
            
            # 限制范围
            humidity = max(0, min(100, humidity))
            
            # 增加读取计数
            self.read_count += 1
            
            return temperature, humidity
            
        except Exception as e:
            print("[SHT30] 读取错误: %s" % e)
            return None, None
    
    async def run(self):
        """主运行循环"""
        if not self.address:
            return
        
        current_time = time.time()
        
        # 检查读取间隔
        if current_time - self.last_read_time < self.read_interval:
            return
        
        self.last_read_time = current_time
        
        # 读取传感器数据
        temp, humidity = await self.read_sensor()
        
        if temp is not None and humidity is not None:
            # 重置错误计数
            self.error_count = 0
            
            # 检查温度变化触发事件
            temp_changed = False
            humidity_changed = False
            
            if self.last_temp is not None:
                if abs(temp - self.last_temp) > 0.5:  # 温度变化超过0.5度
                    temp_changed = True
            else:
                # 第一次读取也触发事件
                temp_changed = True
            
            if self.last_humidity is not None:
                if abs(humidity - self.last_humidity) > 2.0:  # 湿度变化超过2%
                    humidity_changed = True
            else:
                # 第一次读取也触发事件
                humidity_changed = True
            
            # 更新最后读数
            self.last_temp = temp
            self.last_humidity = humidity
            
            # 构建数据
            sensor_data = {
                'temperature': round(temp, 2),
                'humidity': round(humidity, 2),
                'timestamp': current_time,
                'unit_temp': 'celsius',
                'unit_humidity': 'percent',
                'read_count': self.read_count
            }
            
            # 发送数据到后端
            print("[SHT30] [%d] 温度: %.2f°C, 湿度: %.2f%%" % (self.read_count, temp, humidity))
            await self.loader.send_data('sht30', sensor_data)
            
            # 触发温度上报事件
            if temp_changed or humidity_changed:
                event_data = {
                    'event': 'temperature_report',
                    'temperature': round(temp, 2),
                    'humidity': round(humidity, 2),
                    'temp_changed': temp_changed,
                    'humidity_changed': humidity_changed,
                    'timestamp': current_time
                }
                await self.loader.send_data('event', event_data)
                print("[SHT30] 触发温度上报事件 (温度变化:%s, 湿度变化:%s)" % (temp_changed, humidity_changed))
            
            # 检查阈值告警
            if temp > self.temp_threshold:
                await self.loader.send_log('warning', 
                    '温度超过阈值: %.2f°C > %.2f°C' % (temp, self.temp_threshold),
                    sensor_data
                )
            
            if humidity > self.humidity_threshold:
                await self.loader.send_log('warning',
                    '湿度超过阈值: %.2f%% > %.2f%%' % (humidity, self.humidity_threshold),
                    sensor_data
                )
            
            # 更新OLED显示（如果可用）
            try:
                from loader import oled_show
                await oled_show("T:%.1fC H:%.1f%%" % (temp, humidity), 0, 0, clear=True)
                await oled_show("SHT30 #%d" % self.read_count, 0, 16)
                await oled_show("GPIO%d/%d" % (self.sda_pin, self.scl_pin), 0, 32)
            except:
                pass
                
        else:
            # 读取失败
            self.error_count += 1
            print("[SHT30] 读取失败 (%d/%d)" % (self.error_count, self.max_errors))
            
            if self.error_count >= self.max_errors:
                await self.loader.send_log('error', 
                    'SHT30连续读取失败%d次' % self.error_count
                )
                
                # 尝试重新初始化
                print("[SHT30] 尝试重新初始化...")
                await self.reset()
                await asyncio.sleep_ms(100)
                
                # 重新扫描I2C
                devices = self.i2c.scan()
                print("[SHT30] 重新扫描I2C: %s" % [hex(d) for d in devices])
                
                self.error_count = 0
    
    async def handle_read_command(self, params):
        """处理读取命令"""
        temp, humidity = await self.read_sensor()
        
        if temp is not None and humidity is not None:
            return {
                'success': True,
                'temperature': round(temp, 2),
                'humidity': round(humidity, 2),
                'timestamp': time.time(),
                'read_count': self.read_count
            }
        else:
            return {
                'success': False,
                'error': 'Failed to read sensor'
            }
    
    async def handle_config_command(self, params):
        """处理配置命令"""
        changed = False
        
        if 'interval' in params:
            self.read_interval = max(1, min(3600, int(params['interval'])))
            changed = True
            
        if 'temp_threshold' in params:
            self.temp_threshold = float(params['temp_threshold'])
            changed = True
            
        if 'humidity_threshold' in params:
            self.humidity_threshold = float(params['humidity_threshold'])
            changed = True
        
        if changed:
            await self.loader.send_log('info', 'SHT30配置更新', {
                'interval': self.read_interval,
                'temp_threshold': self.temp_threshold,
                'humidity_threshold': self.humidity_threshold
            })
        
        return {
            'success': True,
            'config': {
                'interval': self.read_interval,
                'temp_threshold': self.temp_threshold,
                'humidity_threshold': self.humidity_threshold
            }
        }
    
    async def handle_status_command(self, params):
        """处理状态查询命令"""
        return {
            'success': True,
            'status': {
                'connected': self.address is not None,
                'address': hex(self.address) if self.address else None,
                'sda_pin': self.sda_pin,
                'scl_pin': self.scl_pin,
                'last_temperature': self.last_temp,
                'last_humidity': self.last_humidity,
                'read_count': self.read_count,
                'error_count': self.error_count,
                'read_interval': self.read_interval
            }
        }
    
    async def cleanup(self):
        """清理资源"""
        self.running = False
        # 关闭I2C总线
        if self.i2c:
            try:
                # 释放I2C资源
                self.i2c = None
            except:
                pass
        print("[SHT30] 插件清理完成")

# 插件入口类（loader会查找这个）
Plugin = SHT30